///////////////////////////////////////////////////////////////////////////////
//
// labFisCavend.cc - acquire data for SNS Lab Fis Cavendish experiment
// 
// 1st release: A. Lusiani <a.lusiani@sns.it> April 2003
//

///////////////////////////////////////////////////////////////////////////////
//
// Modifications:
//
//

///////////////////////////////////////////////////////////////////////////////
//
// headers
//

extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <sys/select.h>
}

#ifndef __CINT__
#include "TFile.h"
#include "TApplication.h"
#include "TCanvas.h"
#include "TH1.h"
#include "TH2.h"
#include "TProfile.h"
#include "TAxis.h"
#include "TNtuple.h"
#include "TRandom.h"
#include "TStyle.h"
#endif

#include "aluTermIoAttr.h"
#include "aluClockTicks.h"

#define HS_TIME_XAXIS   (1)
#define SERIALPORT "/dev/ttyS0"
#define MAX(a,b) ((a)<(b)?(b):(a))

///////////////////////////////////////////////////////////////////////////////
//
// function declarations
//


///////////////////////////////////////////////////////////////////////////////
//
// code
//

int main(int argc, char *argv[])
{
  int serPortFd;
  
  //
  // initialize Root
  //
  // code inspired from:
  // - test/hsimple.cxx of Root 3.05.3 package
  //

  TApplication theApp("App", &argc, argv);
  TCanvas *canvas = new TCanvas("canvas", "Cavendish Experiment", 800, 400);

  gStyle->SetTitleOffset(1.4,"Y");

  // Create a new ROOT binary machine independent file.
  // This file is now becoming the current directory.
  TFile hfile("labFisCavend.root", "RECREATE", "Lab Fis: Cavendish");

  //--- voltage proportional to torsion pendulum position
  Float_t vpos;
  Int_t hsecs;

  //--- allocate Root Tree
  TTree *tree = new TTree("nt", "Lab Fisica Cavendish ntuple");
  tree->Branch("vpos", &vpos, "vpos/F");
  tree->Branch("hsecs", &hsecs, "hsecs/I");

  TProfile *hsVposVsTime =
    new TProfile("hsVposVsTime", "Period vs. time",
		 10, -HS_TIME_XAXIS/double(25), HS_TIME_XAXIS,
		 -10, 10);
  hsVposVsTime->SetXTitle("seconds");
  hsVposVsTime->SetYTitle("Volts");
  // hsVposVsTime->SetMaximum(1.990);
  // hsVposVsTime->SetMinimum(1.975);
  hsVposVsTime->Draw("");
  canvas->Update();
  
  //--- allocate object to setup terminal input
  aluTermIoAttr stdinTio(stdin);
  //--- setup terminal for immediate keypress input with no echo
  stdinTio.SetImmedNoecho();

  //--- allocate object to setup serial port input
  serPortFd = open(SERIALPORT, O_RDWR | O_NOCTTY );
  if (serPortFd <0) {perror(SERIALPORT); exit(1); }
  //--- 9600 baud, 8bit, no parity
  aluTermIoAttr serPortTio(serPortFd);
  serPortTio.SetSerialInput();

  int rc;
  bool takeData = false;
  fd_set rdset;
  struct timeval delayTimeout;
  long int timeout = static_cast<long int>(1e6);

  //--- class to get elapsed hundredths of second
  aluClockTicks clockTicks;
  long ticksLast, ticksCurr;
  unsigned int vcount;
  float_t vtime;
  float_t vposMin = 100;
  float_t vposMax = -100;

  fprintf(stderr, "--- Begin monitoring, <space> to begin data-taking --- \n");

  for(vcount=0, ticksLast = clockTicks.GetTicks();;) {
    //--- setup rdset structure to wait for comedi scans and keystrokes
    FD_ZERO(&rdset);
    FD_SET(serPortFd, &rdset);
    FD_SET(fileno(stdin), &rdset);
    delayTimeout.tv_sec = 0;
    delayTimeout.tv_usec = timeout;
    
    //--- wait untill either timeout or some data are available
    rc = select(MAX(serPortFd,fileno(stdin))+1,
		&rdset, NULL, NULL, &delayTimeout);
    if (rc<0) {
      perror("select");
      rc = 1;
      break;
    }

    //--- standard input has some characters available
    if (FD_ISSET(fileno(stdin), &rdset)) {
      int ch = fgetc(stdin);
      if (ch == EOF || feof(stdin)) {
	clearerr(stdin);
	ungetc('\004', stdin);
      } else {
	if (ch == 25) {
	  fprintf(stderr,"--- <control-Y> received, will quit! --- \n");
	  rc = 2;
	  break;
	} else {
	  if (ch == ' ') {
	    fprintf(stderr,"--- Data-taking started --- \n");
	    takeData = true;
	    vcount = 0;
	    ticksCurr = clockTicks.GetTicks() - ticksLast;
	    clockTicks.Reset();
	    ticksLast = clockTicks.GetTicks() - ticksCurr;
	    delete hsVposVsTime;
	    hsVposVsTime =
	      new TProfile("hsVposVsTime", "Period vs. time",
			   10, -HS_TIME_XAXIS/double(25), HS_TIME_XAXIS,
			   -10, 10);
	    tree->Reset();
	  }
	  fprintf(stderr,"--- Press <control-Y> to quit --- \n");
	}
      }

    }
    
    //--- standard input has some characters available
    if (FD_ISSET(serPortFd, &rdset)) {
      char buf[256];
      int num;
      num = read(serPortFd, buf, 255);
      buf[num]=0;
      if (sscanf(buf, "%fV", &vpos) == 1) {
	hsecs = clockTicks.GetTicks();

#if 0
	printf("num=%d, vpos=%f, in=%s\n", num, vpos, buf);
	printf("num=%d, time=%d, vpos=%f\n",
	       vcount, hsecs, vpos);
#endif

	//--- increment count of data received
	vcount++;
	vtime = static_cast<double>(vcount)/100;
	
	if (vposMin > vpos) vposMin = vpos;
	if (vposMax < vpos) vposMax = vpos;

	// if (takeData) tree->Fill();
	//--- always take data
	tree->Fill();
	hsVposVsTime->Fill(vtime, vpos);
	
	if (vposMin == vposMax) {
	  hsVposVsTime->SetMaximum(vposMax+0.01);
	  hsVposVsTime->SetMinimum(vposMin-0.01);
	} else {
	  hsVposVsTime->SetMaximum(vposMax+0.1*(vposMax-vposMin));
	  hsVposVsTime->SetMinimum(vposMin-0.1*(vposMax-vposMin));
	}
	
	if (vtime > 0.90* hsVposVsTime->GetXaxis()->GetXmax()) {
	  hsVposVsTime->LabelsInflate();
	}
      }
    }

    //--- update display each 25 hundredths of second
    ticksCurr=clockTicks.GetTicks();
    if (ticksCurr > ticksLast + 25) {
      ticksLast = ticksCurr;
      hsVposVsTime->Draw("");
      canvas->Update();
    }
  }

  stdinTio.Reset();
  serPortTio.Reset();

  fprintf(stderr,
	  "Please select \"File->Quit Root\" on Root window to quit\n");

  hsVposVsTime->Draw("");
  canvas->Update();
  
  //--- start root GUI: "Exit ROOT" will return to the next statement
  theApp.Run(kTRUE);

  //--- Save all objects in this file
  hfile.Write();

  //--- Close the file.
  hfile.Close();

  return 0;
}
